package com.open.capacity.finance.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.open.capacity.finance.config.Globals;
import com.open.capacity.finance.dao.*;
import com.open.capacity.finance.dto.AuditModelBaseResult;
import com.open.capacity.finance.dto.audit_model_8.AuditModelResult;
import com.open.capacity.finance.dto.audit_model_8.AuditModelStep1;
import com.open.capacity.finance.dto.audit_model_8.AuditModelStep2;
import com.open.capacity.finance.dto.audit_model_8.ExcelData;
import com.open.capacity.finance.entity.*;
import com.open.capacity.finance.service.AbstractAuditMakeResultFactory;
import com.open.capacity.finance.service.AuditModelRuleService;
import com.open.capacity.finance.service.AuditModelService;
import com.open.capacity.finance.util.FileUtil;
import com.open.capacity.nk.dto.NKReceiptDto;
import com.open.capacity.reptile.dao.GdszczPayVoucherBillDao;
import com.open.capacity.reptile.entity.GdszczPayVoucherBill;
import com.open.capacity.user.model.SysCompany;
import com.open.capacity.user.service.SysCompanyService;
import com.open.capacity.utils.BigDecimalUtil;
import com.open.capacity.utils.StringUtil;
import com.open.capacity.utils.encrypt.Md5Utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * TODO 补充含有经济分类的预算执行情况表
 *
 * @param <T>
 */
@Slf4j
@Service
public class AuditModelActuator8ServiceImpl<T extends AuditModelBaseResult> extends AbstractAuditMakeResultFactory {


    @Autowired
    private AuditAnalysisDao analysisDao;

    @Autowired
    private GdszczPayVoucherBillDao basePayDao;

    @Autowired
    private SourceBaseBudgetTotal12Dao baseBudgetTotal12Dao;

    @Autowired
    private SourceBaseKjkmExtDao kjkmExtDao;
    @Autowired
    private SourceBaseCustomItemDao customItemDao;
    @Autowired
    private SourceBaseCustomValueDao customValueDao;

    @Autowired
    private AuditModelRuleService ruleService;
    @Autowired
    private AuditModelService modelService;

    @Autowired
    private SysCompanyService companyService;

    //部门支出经济分类
    private static final String CUSTOMITEMCODE = "EXPECO";
    //6公
    private static final String SIXPUBLICFUNDS = "30212,30217,30231,30215,30216,30211";

    @Override
    protected T analyseResultData(String modelId, Map param) {
        String companyId = param.containsKey("companyId") ? param.get("companyId").toString() : "";
        param.put("ztId", "101");
        Integer kjnd = param.containsKey("kjnd") ? Integer.parseInt(param.get("kjnd").toString()) : null;
        AuditModelResult result = new AuditModelResult();
        AuditModel model = modelService.findById(modelId);
        Assert.notNull(model, "找不到该模型");
        List<AuditModelRule> rules = ruleService.findByModelId(modelId);

        AtomicReference<String> jjflCodes = new AtomicReference<>(SIXPUBLICFUNDS);
        rules.forEach(t -> {
            if (t.getConfKey().equals("jjflCodes")) {
                jjflCodes.set(t.getConfValue());
            }
            if (t.getConfKey().equals("zys")) {
                param.put(t.getConfKey(), t.getConfValue().split(","));
            } else {
                param.put(t.getConfKey(), t.getConfValue());
            }
        });
        result.setModelName(model.getCodeAndName());
        if (!param.containsKey("flag")) { //默认查异常数据
            param.put("flag", 1);
        }

        //6公经费辅助项code
        param.put("jjflcodes", jjflCodes.toString().split(","));

        List<AuditModelStep1> pzList = analysisDao.model8(param);

        //执行支付
        List<GdszczPayVoucherBill> excuteList = basePayDao.findAll(param);

        //预算批复
        List<SourceBaseBudgetTotal12> total12List = baseBudgetTotal12Dao.findAll(param);


        HashMap<String, List<SourceBaseBudgetTotal12>> total12Map = total12List.stream().collect(Collectors.groupingBy(t -> t.getCompanyId(), HashMap::new, Collectors.toList()));
        HashMap<String, List<GdszczPayVoucherBill>> excuteMap = excuteList.stream().collect(Collectors.groupingBy(t -> t.getAgencyCode(), HashMap::new, Collectors.toList()));
        HashMap<String, List<AuditModelStep1>> pzMap = pzList.stream().collect(Collectors.groupingBy(t -> t.getCompanyId(), HashMap::new, Collectors.toList()));


        List<AuditModelStep2> step2List = new ArrayList<>();

        //遍历全部机构
        Map<String, Object> params = new HashMap<>();
        if (StringUtil.isNotEmpty(companyId) && !companyId.equals("-1")) {
            params.put("id", companyId);
        }
        List<SysCompany> companys = companyService.findAll(params);
        AuditModelStep2 item;
        for (SysCompany company : companys) {
            item = new AuditModelStep2();
            item.setKjnd(kjnd);
            item.setFlag(0);
            item.setCompanyId(company.getId());
            item.setCompanyName(company.getFullName());

            //30211
            Optional<SourceBaseBudgetTotal12> total30211 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30211")).findFirst();
            if (total30211.isPresent()) {
                item.setTotal30211(total30211.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30211 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30211")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30211)) {
                item.setExcute30211(excute30211.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30211 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30211")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30211)) {
                item.setPz30211(pz30211.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            //30212
            Optional<SourceBaseBudgetTotal12> total30212 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30212")).findFirst();
            if (total30212.isPresent()) {
                item.setTotal30212(total30212.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30212 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30212")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30212)) {
                item.setExcute30212(excute30212.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30212 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30212")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30212)) {
                item.setPz30212(pz30212.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            //30215
            Optional<SourceBaseBudgetTotal12> total30215 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30215")).findFirst();
            if (total30215.isPresent()) {
                item.setTotal30215(total30215.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30215 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30215")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30215)) {
                item.setExcute30215(excute30215.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30215 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30215")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30215)) {
                item.setPz30215(pz30215.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            //30216
            Optional<SourceBaseBudgetTotal12> total30216 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30216")).findFirst();
            if (total30216.isPresent()) {
                item.setTotal30216(total30216.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30216 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30216")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30216)) {
                item.setExcute30216(excute30216.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30216 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30216")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30216)) {
                item.setPz30216(pz30216.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            //30217
            Optional<SourceBaseBudgetTotal12> total30217 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30217")).findFirst();
            if (total30217.isPresent()) {
                item.setTotal30217(total30217.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30217 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30217")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30217)) {
                item.setExcute30217(excute30217.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30217 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30217")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30217)) {
                item.setPz30217(pz30217.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            //30231
            Optional<SourceBaseBudgetTotal12> total30231 = total12List.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().equals("30231")).findFirst();
            if (total30231.isPresent()) {
                item.setTotal30231(total30231.get().getTotalMoney());
            }

            List<GdszczPayVoucherBill> excute30231 = excuteList.stream().filter(t -> t.getAgencyCode().equals(company.getId()) && StringUtil.isNotEmpty(t.getDepBgtEcoCode()) && t.getDepBgtEcoCode().equals("30231")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(excute30231)) {
                item.setExcute30231(excute30231.stream().map(GdszczPayVoucherBill::getPayAmt).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            List<AuditModelStep1> pz30231 = pzList.stream().filter(t -> t.getCompanyId().equals(company.getId()) && t.getJjflCode().startsWith("30231")).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(pz30231)) {
                item.setPz30231(pz30231.stream().map(AuditModelStep1::getJ).reduce(BigDecimal.ZERO, BigDecimal::add));
            }

            if (BigDecimalUtil.getData(item.getPz30231()).compareTo(BigDecimalUtil.getData(item.getExcute30231())) != 0 || BigDecimalUtil.getData(item.getPz30217()).compareTo(BigDecimalUtil.getData(item.getExcute30217())) != 0 || BigDecimalUtil.getData(item.getPz30212()).compareTo(BigDecimalUtil.getData(item.getExcute30212())) != 0 || BigDecimalUtil.getData(item.getPz30215()).compareTo(BigDecimalUtil.getData(item.getExcute30215())) != 0 || BigDecimalUtil.getData(item.getPz30216()).compareTo(BigDecimalUtil.getData(item.getExcute30216())) != 0 || BigDecimalUtil.getData(item.getPz30211()).compareTo(BigDecimalUtil.getData(item.getExcute30211())) != 0) {
                item.setFlag(1);
            }
            step2List.add(item);
        }


        //只显示异常
        Integer flag = param.containsKey("flag") && StringUtil.isNotEmpty(param.get("flag").toString()) ? Integer.parseInt(param.get("flag").toString()) : 0;
        if (flag.equals(1)) {
            step2List = step2List.stream().filter(t -> t.getFlag().equals(1)).collect(Collectors.toList());
        }
        result.setCompanyId(companyId);
        step2List.forEach(t -> {
            Optional<SysCompany> first = Globals.companyList.stream().filter(c -> c.getId().equals(t.getCompanyId())).findFirst();
            if (first.isPresent()) {
                t.setCompanyName(first.get().getFullName());
            }
        });

        step2List.sort(Comparator.comparing(AuditModelStep2::getCompanyId).thenComparing(AuditModelStep2::getKjnd));

        result.setStep1List(step2List);
        if (CollectionUtils.isEmpty(step2List)) {
            result.setExists(0);
        } else {
            result.setExists(1);

            //持久化本地
            persistence(result);
        }

        return (T) result;
    }

    @Override
    protected int persistence(AuditModelBaseResult data) {
        int result = 0;

        String content = JSON.toJSONString(data, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);

        data.setResultFileMd5(Md5Utils.hash(content));
        data.setResultFileUrl("/upload/analyse/model8_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");

        Writer write = null;
        try {
            File file = new File(fileuploadPath + "/upload/analyse/model8_" + data.getCompanyId() + "_" + data.getResultFileMd5() + ".json");
            if (file.exists()) {
                file.delete();
            }
            file.createNewFile();
            // 写入文件
            write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
            write.write(content);
            write.flush();
            write.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                write.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
        return result;
    }

    @Override
    public T getData(String modelId, Map param) {
        return analyseResultData(modelId, param);
    }

    /**
     * 从本地加载数据
     */
    public AuditModelResult getDataFromJson(String jsonUrl, Map param) {
        AuditModelResult result = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            result = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        filterCondition(result, param);

        return result;
    }

    /**
     * 本地json数据条件过滤
     */
    private AuditModelResult filterCondition(AuditModelResult orig, Map param) {
        AuditModelResult result = orig;
        String companyId = MapUtils.getString(param, "companyId");
        Integer kjnd = MapUtils.getInteger(param, "kjnd");

       Integer flag = MapUtils.getInteger(param, "flag");

        List<AuditModelStep2> list = orig.getStep1List();
        if (StringUtil.isNotEmpty(companyId)) {
            list = list.stream().filter(t -> t.getCompanyId().equals(companyId)).collect(Collectors.toList());
        }
        if (kjnd != null) {
            list = list.stream().filter(t -> t.getKjnd().equals(kjnd)).collect(Collectors.toList());
        }
        if (flag != null) {
            list = list.stream().filter(t -> t.getFlag().equals(flag)).collect(Collectors.toList());
        }

        result.setStep1List(list);

        return result;
    }

    public void exportExcel(String modelId, Map params, HttpServletResponse response) {
        AuditModelResult t = (AuditModelResult) analyseResultData(modelId, params);

        export(t, response);
    }

    @Override
    public void exportExcelFromJson(String modelId, String jsonUrl, HttpServletResponse response) {
        AuditModelResult t = null;
        try {
            String json = FileUtil.readTxtToJson(fileuploadPath + jsonUrl);
            t = JSONObject.parseObject(json, new TypeReference<AuditModelResult>() {
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

        export(t, response);
    }

    private void export(AuditModelResult t, HttpServletResponse response) {
        if (StringUtil.isNotEmpty(t.getStep1List())) {
            List<ExcelData> result = t.getStep1List().stream().map(item -> {
                ExcelData model = new ExcelData();
                BeanUtils.copyProperties(item, model);
                return model;
            }).collect(Collectors.toList());

            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", "attachment;filename=download_8.xls");
            OutputStream ouputStream = null;
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), ExcelData.class, result);
            try {
                ouputStream = response.getOutputStream();
                workbook.write(ouputStream);
            } catch (Exception e) {
                throw new RuntimeException("系统异常");
            } finally {
                try {
                    if (ouputStream != null) {
                        ouputStream.flush();
                        ouputStream.close();
                    }
                } catch (Exception e) {
                    throw new RuntimeException("系统异常");
                }
            }
        }
    }
}
